/*
  interval.h  -  definitions of interval functions

  This file is part of the IVL Library (LIBIVL).
  
  LIBIVL - library for interval computations 
  
  Copyright (c) 2007 Mateusz Paprocki
  
  The IVL Library is free software; you can redistribute it and/or modify
  it under the terms of the GNU Lesser General Public License as published
  by the Free Software Foundation; either version 2.1 of the License, or
  (at your option) any later version.

  The IVL Library is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
  License for more details.

  You should have received a copy of the GNU Lesser General Public License
  along with the IVL Library; see the file LICENCE.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#ifndef __IVL_H__
#define __IVL_H__

#include <stdio.h>
#include <mpfr.h>

typedef enum {
	IVL_UNDEFINED =  0,		/* invalid bounds */
	IVL_TOTAL     =  1,     /* proper bounds  */
	IVL_PARTIAL   =  3,     /* proper bounds  */
} ivl_domain_t;

typedef struct {
	__mpfr_struct left;		/* lower bound */
	__mpfr_struct right;	/* upper bound */
	ivl_domain_t domain;
} __ivl_struct;

typedef __ivl_struct ivl_t[1];
typedef __ivl_struct *ivl_ptr;
typedef __gmp_const __ivl_struct *ivl_srcptr;

/* Helpers for readability reason. */

#define IVL_LEFT(x) (&((x)->left))
#define IVL_RIGHT(x) (&((x)->right))

#define DOM_UNDEFINED(x) (((x)->domain) == IVL_UNDEFINED)
#define DOM_PARTIAL(x) (((x)->domain) == IVL_PARTIAL)
#define DOM_TOTAL(x) (((x)->domain) == IVL_TOTAL)

#define IVL_NAN_P(x) (mpfr_nan_p(&((x)->left)) || mpfr_nan_p(&((x)->right)))

#define IVL_VALIDATE(x)                         \
	if ((DOM_UNDEFINED(x)) || (IVL_NAN_P(x))) {	\
		mpfr_set_nan(&((x)->left));             \
		mpfr_set_nan(&((x)->right));            \
	}

#define IVL_CHECK(x, dom, inexact, inexact_left, inexact_right) \
	if (IVL_NAN_P(x)) {                                         \
		(x)->domain = IVL_UNDEFINED;                            \
	} else {                                                    \
		(x)->domain = (dom);                                    \
			                                                    \
		if (inexact_left)                                       \
			(inexact) += 1;                                     \
		if (inexact_right)                                      \
			(inexact) += 2;                                     \
	}

#define IVL_CHECK_NO_DOM(x, inexact, inexact_left, inexact_right) \
	if (IVL_NAN_P(x)) {                                           \
		(x)->domain = IVL_UNDEFINED;                              \
	} else {                                                      \
		if (inexact_left)                                         \
			(inexact) += 1;                                       \
		if (inexact_right)                                        \
			(inexact) += 2;                                       \
	}
	
#define SGN_NEGATIVE (-1)
#define SGN_ZERO     (0)
#define SGN_POSITIVE (1)

#define BITS_PER_LIMB (GMP_NUMB_BITS + GMP_NAIL_BITS)
	
/* ivl_base.c */

extern void ivl_init(ivl_ptr ivl);
extern void ivl_init2(ivl_ptr ivl, mp_prec_t prec);
extern void ivl_clear(ivl_ptr ivl);

extern void ivl_swap(ivl_ptr x, ivl_ptr y);

extern void ivl_set_prec(ivl_ptr ivl, mp_prec_t prec);
extern mp_prec_t ivl_get_prec(ivl_srcptr ivl);

extern int ivl_set_domain(ivl_ptr ivl, ivl_domain_t dom);
extern ivl_domain_t ivl_get_domain(ivl_srcptr ivl);

extern int ivl_get_left(mpfr_ptr fr, ivl_srcptr ivl);
extern int ivl_get_right(mpfr_ptr fr, ivl_srcptr ivl);

extern double ivl_get_left_d(ivl_srcptr ivl);
extern double ivl_get_right_d(ivl_srcptr ivl);

extern void ivl_print(FILE *fp, ivl_srcptr ivl, int base, size_t digits);

extern int ivl_length(mpfr_t len, ivl_srcptr ivl);

extern int ivl_has_zero(ivl_srcptr ivl);

extern int ivl_point_p(ivl_srcptr ivl);
extern int ivl_point_inf_p(ivl_srcptr ivl);

extern int ivl_bounded_p(ivl_srcptr ivl);
extern int ivl_unbounded_p(ivl_srcptr ivl);

/* ivl_make.c */

extern int ivl_make_u(ivl_ptr ivl, unsigned long x, unsigned long y);
extern int ivl_make_s(ivl_ptr ivl, long x, long y);
extern int ivl_make_d(ivl_ptr ivl, double x, double y);
extern int ivl_make_z(ivl_ptr ivl, mpz_srcptr x, mpz_srcptr y);
extern int ivl_make_q(ivl_ptr ivl, mpq_srcptr x, mpq_srcptr y);
extern int ivl_make_f(ivl_ptr ivl, mpfr_srcptr x, mpfr_srcptr y);

extern int ivl_make_str(ivl_ptr ivl, const char *x, const char *y, int base);

extern void ivl_make_unbounded(ivl_ptr ivl, ivl_domain_t dom);

/* ivl_set.c */

extern int ivl_set(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_set_u(ivl_ptr ivl, unsigned long op);
extern int ivl_set_s(ivl_ptr ivl, long op);
extern int ivl_set_d(ivl_ptr ivl, double op);
extern int ivl_set_z(ivl_ptr ivl, mpz_srcptr op);
extern int ivl_set_q(ivl_ptr ivl, mpq_srcptr op);
extern int ivl_set_f(ivl_ptr ivl, mpfr_srcptr op);

extern int ivl_set_str(ivl_ptr ivl, const char *str, int base);

extern void ivl_set_inf(ivl_ptr ivl, long sign);

/* ivl_expand.c */

extern int ivl_expand_u(ivl_ptr ivl, unsigned long op);
extern int ivl_expand_s(ivl_ptr ivl, long op);
extern int ivl_expand_d(ivl_ptr ivl, double op);
extern int ivl_expand_z(ivl_ptr ivl, mpz_srcptr op);
extern int ivl_expand_q(ivl_ptr ivl, mpq_srcptr op);
extern int ivl_expand_f(ivl_ptr ivl, mpfr_srcptr op);

extern int ivl_expand_str(ivl_ptr ivl, const char *str, int base);

extern void ivl_expand_inf(ivl_ptr ivl, long sign);

/* ivl_cmp.c */

extern int ivl_equal_p(ivl_srcptr x, ivl_srcptr y);

extern int ivl_greater_p(ivl_srcptr x, ivl_srcptr y);
extern int ivl_greaterequal_p(ivl_srcptr x, ivl_srcptr y);

extern int ivl_less_p(ivl_srcptr x, ivl_srcptr y);
extern int ivl_lessequal_p(ivl_srcptr x, ivl_srcptr y);

extern int ivl_strictly_inside_p(ivl_srcptr x, ivl_srcptr y);
extern int ivl_inside_p(ivl_srcptr x, ivl_srcptr y);

extern int ivl_is_inside_u(ivl_srcptr ivl, unsigned long x);
extern int ivl_is_inside_s(ivl_srcptr ivl, long x);
extern int ivl_is_inside_d(ivl_srcptr ivl, double x);
extern int ivl_is_inside_z(ivl_srcptr ivl, mpz_srcptr x);
extern int ivl_is_inside_q(ivl_srcptr ivl, mpq_srcptr x);
extern int ivl_is_inside_f(ivl_srcptr ivl, mpfr_srcptr x);

extern int ivl_is_strictly_inside_u(ivl_srcptr ivl, unsigned long x);
extern int ivl_is_strictly_inside_s(ivl_srcptr ivl, long x);
extern int ivl_is_strictly_inside_d(ivl_srcptr ivl, double x);
extern int ivl_is_strictly_inside_z(ivl_srcptr ivl, mpz_srcptr x);
extern int ivl_is_strictly_inside_q(ivl_srcptr ivl, mpq_srcptr x);
extern int ivl_is_strictly_inside_f(ivl_srcptr ivl, mpfr_srcptr x);

extern int ivl_is_equal_u(ivl_srcptr ivl, unsigned long x);
extern int ivl_is_equal_s(ivl_srcptr ivl, long x);
extern int ivl_is_equal_d(ivl_srcptr ivl, double x);
extern int ivl_is_equal_z(ivl_srcptr ivl, mpz_srcptr x);
extern int ivl_is_equal_q(ivl_srcptr ivl, mpq_srcptr x);
extern int ivl_is_equal_f(ivl_srcptr ivl, mpfr_srcptr x);

/* ivl_sets.c */

extern int ivl_union(ivl_ptr x, ivl_srcptr y);
extern int ivl_intersect(ivl_ptr x, ivl_srcptr y);

/* ivl_add.c */

extern int ivl_add(ivl_ptr ivl, ivl_srcptr x, ivl_srcptr y);

extern int ivl_add_u(ivl_ptr ivl, ivl_srcptr x, unsigned long y);
extern int ivl_add_s(ivl_ptr ivl, ivl_srcptr x, long y);
extern int ivl_add_d(ivl_ptr ivl, ivl_srcptr x, double y);
extern int ivl_add_z(ivl_ptr ivl, ivl_srcptr x, mpz_srcptr y);
extern int ivl_add_q(ivl_ptr ivl, ivl_srcptr x, mpq_srcptr y);
extern int ivl_add_f(ivl_ptr ivl, ivl_srcptr x, mpfr_srcptr y);

/* ivl_sub.c */

extern int ivl_sub(ivl_ptr ivl, ivl_srcptr x, ivl_srcptr y);

extern int ivl_sub_u(ivl_ptr ivl, ivl_srcptr x, unsigned long y);
extern int ivl_sub_s(ivl_ptr ivl, ivl_srcptr x, long y);
extern int ivl_sub_d(ivl_ptr ivl, ivl_srcptr x, double y);
extern int ivl_sub_z(ivl_ptr ivl, ivl_srcptr x, mpz_srcptr y);
extern int ivl_sub_q(ivl_ptr ivl, ivl_srcptr x, mpq_srcptr y);
extern int ivl_sub_f(ivl_ptr ivl, ivl_srcptr x, mpfr_srcptr y);

extern int ivl_u_sub(ivl_ptr ivl, unsigned long x, ivl_srcptr y);
extern int ivl_s_sub(ivl_ptr ivl, long x, ivl_srcptr y);
extern int ivl_d_sub(ivl_ptr ivl, double x, ivl_srcptr y);
extern int ivl_z_sub(ivl_ptr ivl, mpz_srcptr x, ivl_srcptr y);
extern int ivl_q_sub(ivl_ptr ivl, mpq_srcptr x, ivl_srcptr y);
extern int ivl_f_sub(ivl_ptr ivl, mpfr_srcptr x, ivl_srcptr y);

/* ivl_mul.c */

extern int ivl_mul(ivl_ptr ivl, ivl_srcptr x, ivl_srcptr y);

extern int ivl_mul_u(ivl_ptr ivl, ivl_srcptr x, unsigned long y);
extern int ivl_mul_s(ivl_ptr ivl, ivl_srcptr x, long y);
extern int ivl_mul_d(ivl_ptr ivl, ivl_srcptr x, double y);
extern int ivl_mul_z(ivl_ptr ivl, ivl_srcptr x, mpz_srcptr y);
extern int ivl_mul_q(ivl_ptr ivl, ivl_srcptr x, mpq_srcptr y);
extern int ivl_mul_f(ivl_ptr ivl, ivl_srcptr x, mpfr_srcptr y);

extern int ivl_mul_2exp(ivl_ptr ivl, ivl_srcptr x, unsigned long exp);

/* ivl_div.c */

extern int ivl_div(ivl_ptr ivl, ivl_srcptr x, ivl_srcptr y);

extern int ivl_div_u(ivl_ptr ivl, ivl_srcptr x, unsigned long y);
extern int ivl_div_s(ivl_ptr ivl, ivl_srcptr x, long y);
extern int ivl_div_d(ivl_ptr ivl, ivl_srcptr x, double y);
extern int ivl_div_z(ivl_ptr ivl, ivl_srcptr x, mpz_srcptr y);
extern int ivl_div_q(ivl_ptr ivl, ivl_srcptr x, mpq_srcptr y);
extern int ivl_div_f(ivl_ptr ivl, ivl_srcptr x, mpfr_srcptr y);

extern int ivl_div_2exp(ivl_ptr ivl, ivl_srcptr op, unsigned long exp);

extern int ivl_u_div(ivl_ptr ivl, unsigned long x, ivl_srcptr y);
extern int ivl_s_div(ivl_ptr ivl, long x, ivl_srcptr y);

/* ivl_pow.c */

extern int ivl_pow(ivl_ptr ivl, ivl_srcptr x, ivl_srcptr y);

extern int ivl_pow_u(ivl_ptr ivl, ivl_srcptr x, unsigned long y);
extern int ivl_pow_s(ivl_ptr ivl, ivl_srcptr x, long y);
extern int ivl_pow_z(ivl_ptr ivl, ivl_srcptr x, mpz_srcptr y);

extern int ivl_u_pow(ivl_ptr ivl, unsigned long x, ivl_srcptr y);

/* ivl_const.c */

extern int ivl_const_pi(ivl_ptr ivl);

/* ivl_funcs.c */

extern int ivl_signum(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_neg(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_abs(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_floor(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_ceil(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_sqrt(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_exp(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_exp2(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_exp10(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_log(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_log2(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_log10(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_min(ivl_ptr ivl, ivl_srcptr x, ivl_srcptr y);
extern int ivl_max(ivl_ptr ivl, ivl_srcptr x, ivl_srcptr y);

/* ivl_trig.c */

extern int ivl_sin(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_cos(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_tan(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_csc(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_sec(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_cot(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_asin(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_acot(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_atan(ivl_ptr ivl, ivl_srcptr op);

/* ivl_hyper.c */

extern int ivl_sinh(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_cosh(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_tanh(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_csch(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_sech(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_coth(ivl_ptr ivl, ivl_srcptr op);

extern int ivl_asinh(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_acoth(ivl_ptr ivl, ivl_srcptr op);
extern int ivl_atanh(ivl_ptr ivl, ivl_srcptr op);

/* Useful macros */

#define ivl_init_make_u(ivl, x, y) \
	ivl_init(ivl); ivl_make_u(ivl, x, y);
	
#define ivl_init_make_s(ivl, x, y) \
	ivl_init(ivl); ivl_make_s(ivl, x, y);
	
#define ivl_init_make_d(ivl, x, y) \
	ivl_init(ivl); ivl_make_d(ivl, x, y);
	
#define ivl_init_make_z(ivl, x, y) \
	ivl_init(ivl); ivl_make_z(ivl, x, y);
	
#define ivl_init_make_q(ivl, x, y) \
	ivl_init(ivl); ivl_make_q(ivl, x, y);
	
#define ivl_init_make_f(ivl, x, y) \
	ivl_init(ivl); ivl_make_f(ivl, x, y);
	

#define ivl_init_set_u(ivl, x) \
	ivl_init(ivl); ivl_set_u(ivl, x);
	
#define ivl_init_set_s(ivl, x) \
	ivl_init(ivl); ivl_set_s(ivl, x);
	
#define ivl_init_set_d(ivl, x) \
	ivl_init(ivl); ivl_set_d(ivl, x);
	
#define ivl_init_set_z(ivl, x) \
	ivl_init(ivl); ivl_set_z(ivl, x);
	
#define ivl_init_set_q(ivl, x) \
	ivl_init(ivl); ivl_set_q(ivl, x);
	
#define ivl_init_set_f(ivl, x) \
	ivl_init(ivl); ivl_set_f(ivl, x);	
	
	
#define ivl_inv(ivl, x) \
	ivl_ui_div(ivl, 1, x);
	
#define ivl_set_default_prec mpfr_set_default_prec

#endif /* not __IVL_H__ */

